﻿using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Net8.Project.Common;
using Net8.Project.Common.Core;
using Net8.Project.Common.Extensions;
using Serilog;

namespace Net8.Project.Extension.IPLimit
{
    /// <summary>
    /// IP 限流服务注册扩展
    /// </summary>
    public static class IpPolicyRateLimit
    {
        /// <summary>
        /// 注册 IP 限流相关服务（AspNetCoreRateLimit）
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <exception cref="ArgumentNullException">services 为空时抛出</exception>
        public static void AddIpPolicyRateLimitSetup(this IServiceCollection services)
        {
            ArgumentNullException.ThrowIfNull(services);

            // 从 appsettings.json 加载 IP 限流配置
            services.Configure<IpRateLimitOptions>(App.Configuration.GetSection("IpRateLimiting"));

            // 注入分布式策略存储（策略与计数器）
            services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();

            // 注入异步锁处理策略，提升并发下的性能
            services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
            // the clientId/clientIp resolvers use it.
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            // 注入限流配置（包含解析器、计数器 key 构建等）
            services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
        }

        /// <summary>
        /// 注册中间件以启用 IP 限流
        /// </summary>
        /// <param name="app"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void UseIpPolicyRateLimitSetup(this IApplicationBuilder app)
        {
            if (app == null) throw new ArgumentNullException(nameof(app));

            try
            {
                if (AppSettings.App("Middleware", "IpRateLimit", "Enabled").ObjToBool())
                {
                    app.UseIpRateLimiting();
                }
            }
            catch (Exception e)
            {
                Log.Error($"Error occured limiting ip rate.\n{e.Message}");
                throw;
            }
        }
    }
}
